#include "Connection.h"

using namespace boost::asio;
Connection::Connection(io_context& io_context, ip::tcp::resolver::results_type results_type) : socket{io_context}
{
    boost::asio::async_connect(socket, results_type, [this] (const boost::system::error_code& error, ip::tcp::endpoint) {
        if (!error) {
            asyncReceive();
            return;
        }
        else {
            throw std::runtime_error("Failed to open socket");
        }
        });
};

std::shared_ptr<Connection> Connection::create(io_context& io_context, ip::tcp::resolver::results_type results_type) {
    return std::make_shared<Connection>(io_context, results_type);
}

/**
 * @param packet
 */
void Connection::asyncSend(const Packet& packet) {
    auto self(shared_from_this());
    auto write_buffer = std::make_shared<std::vector<char>>(Packet::serialize(packet));
    boost::asio::async_write(socket, boost::asio::buffer(*write_buffer, sizeof(Packet)), [self, this, write_buffer] (const boost::system::error_code& error, std::size_t bytes_transferred) {
        if (!error && bytes_transferred == sizeof(Packet)) {
            return;
        }
        else {
            close();
            throw std::runtime_error("Failed to send packet");
        }
        });
}

/**
 * @return void
 */
void Connection::asyncReceive() {
    auto self(shared_from_this());
    boost::asio::async_read(socket, boost::asio::buffer(read_buffer, sizeof(Packet)), [self, this] (const boost::system::error_code& error, std::size_t bytes_transferred) {
        if (!error && bytes_transferred == sizeof(Packet)) {
            notifier.onPacketReceived(Packet::deserialize(read_buffer));
            asyncReceive();
        }
        else {
            close();
            throw std::runtime_error("Failed to receive packet");
        }
        });
};